<?php


namespace app\common\command;


use GatewayWorker\BusinessWorker;
use GatewayWorker\Gateway;
use think\console\Command;
use think\console\Input;
use think\console\Output;
use think\Env;
use think\console\input\Argument;
use think\console\input\Option;
use \Workerman\Worker;
use \GatewayWorker\Register;

// require_once __DIR__ . '/../../../vendor/autoload.php';

class Workerman extends Command
{
    /**
     * 命令配置
     */
    protected function configure()
    {
        $this->setName('worker:common')
            ->addArgument('service', Argument::OPTIONAL, "all|register|gateway|busunessworker", 'all')
            ->addArgument('action', Argument::OPTIONAL, "start|stop|restart|reload|status|connections", 'start')
            ->addOption('daemon', 'd', Option::VALUE_NONE, 'Run the workerman server in daemon mode.') //supervisor启动去掉 -d的参数，这里要注意，为了能看日志，能启停，启动参数不要加 -d；但是如果查看状态里的qps的话，不能再tank上获取，只能进入到 某一台服务器里，然后 增加 -d 参数；
            ->setDescription('启动workerman服务');
    }

    /**
     * 执行任务
     * @param Input $input
     * @param Output $output
     * @return int|void|null
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     * @throws \think\exception\PDOException
     */
    protected function execute(Input $input, Output $output)
    {

        $service = $input->getArgument('service');
        $action = $input->getArgument('action');

        if (!in_array($service, ['all', 'register', 'gateway', 'busunessworker']))
        {
            $output->writeln("Invalid argument service:{$service}, Expected all|register|gateway|busunessworker .");
            exit(1);
        }

        if (!in_array($action, ['start', 'stop', 'reload', 'restart', 'status', 'connections']))
        {
            $output->writeln("Invalid argument action:{$action}, Expected start|stop|restart|reload|status|connections .");
            exit(1);
        }


        //获取env
        $register_ip = config('websocket.register_ip');//注册服务的ip地址
        $register_address = $register_ip . ':' . config('websocket.register_port');//服务注册地址

        //根据env里的host-》name设置，$gateway_ip地址
        if (Env::get('host.name') !== 'online')
        {
            $gateway_ip = '127.0.0.1';//因为local、beta、tmp zhunle_api和zhunle_admin项目都部署在了同一台机器上
        } else
        {//线上是分布式部署，配置为 为当前服务器的内网ip（参考：http://doc2.workerman.net/how-distributed.html）
            $gateway_ip = getHostByName(getHostName());
        }


        $msg = date('Y-m-d H:i:s') . ' Workerman execute $service=' . $service . ',$action=' . $action . ',$register_address=' . $register_address . ',$gateway_ip=' . $gateway_ip;
        $output->writeln($msg);

        switch ($service)
        {
            case 'all':
            {
                if (Env::get('host.name') !== 'online')
                {
                    $this->conf_register($register_address);
                    $this->conf_gateway($register_address, $gateway_ip);
                    $this->conf_businussworker($register_address);
                } else
                {
                    if ($register_ip === $gateway_ip)
                    {//ip地址相同，那么此服务器只是注册服务，都正常启动吧，否则 zhunle_api发送消息失败，zhunle_admin发送消息成功
                        $this->conf_register($register_address);
                        $this->conf_gateway($register_address, $gateway_ip);
                        $this->conf_businussworker($register_address);
                    } else
                    {
                        $this->conf_gateway($register_address, $gateway_ip);
                        $this->conf_businussworker($register_address);
                    }
                }

                break;
            }
            case 'register':
            {
                $this->conf_register($register_address);
                break;
            }
            case 'gateway':
            {
                $this->conf_gateway($register_address, $gateway_ip);
                break;
            }
            case 'busunessworker':
            {
                $this->conf_businussworker($register_address);
                break;
            }
        }

        Worker::$daemonize = false;
        $output->writeln('Worker::$daemonize=' . json_encode(Worker::$daemonize));
        //启动workerman
        Worker::runAll();
    }

    /**
     * 开始配置注册服务地址
     * @param string $register_address 注册服务地址
     */
    private function conf_register($register_address = '127.0.0.1:1236')
    {
        // register 服务必须是text协议
        new Register('text://' . $register_address);
    }

    /**
     * 开始配置gateway
     * @param string $register_address 注册地址
     * @param string $gateway_ip 单机部署默认 127.0.0.1，网关的地址（$gateway->lanIp），分布式部署时请设置成内网ip（非127.0.0.1）
     */
    private function conf_gateway($register_address = '127.0.0.1:1236', $gateway_ip = '127.0.0.1')
    {
        // gateway 进程
        //        $gateway = new Gateway("Websocket://0.0.0.0:9997");
        $gateway = new Gateway("Websocket://0.0.0.0:9997");
        // 设置名称，方便status时查看
        $gateway->name = 'ChatGateway';
        // 设置进程数，gateway进程数建议与cpu核数相同
        $gateway->count = 1;
        // 分布式部署时请设置成内网ip（非127.0.0.1）
        //$gateway->lanIp = '127.0.0.1';
        $gateway->lanIp = $gateway_ip;
        //$gateway->lanIp = getHostByName(getHostName());
        //$gateway->lanIp = '172.24.9.121';
        // 内部通讯起始端口。假如$gateway->count=4，起始端口为2300
        // 则一般会使用2300 2301 2302 2303 4个端口作为内部通讯端口
        $gateway->startPort = 2300;
        // 心跳间隔
        // 心跳数据(http://doc2.workerman.net/heartbeat.html)
        //服务端发送心跳数据，不推荐
        //其中pingNotResponseLimit = 0代表服务端允许客户端不发送心跳，服务端不会因为客户端长时间没发送数据而断开连接。
        //如果pingNotResponseLimit = 1，则代表客户端必须定时发送数据给服务端，否则pingNotResponseLimit*pingInterval=55秒内没有任何数据发来则关闭对应连接，并触发onClose
//        $gateway->pingInterval = 10;
//        $gateway->pingData = '{"action":"ping"}';

        //客户端发送心跳数据，推荐（？这种方式，总是断掉，不知道为啥）
        $gateway->pingInterval = 55;
        $gateway->pingNotResponseLimit = 1;
        $gateway->pingData = '';

        // 服务注册地址
        $gateway->registerAddress = $register_address;

        /*
        // 当客户端连接上来时，设置连接的onWebSocketConnect，即在websocket握手时的回调
        $gateway->onConnect = function($connection)
        {
            $connection->onWebSocketConnect = function($connection , $http_header)
            {
                // 可以在这里判断连接来源是否合法，不合法就关掉连接
                // $_SERVER['HTTP_ORIGIN']标识来自哪个站点的页面发起的websocket链接
                if($_SERVER['HTTP_ORIGIN'] != 'http://chat.workerman.net')
                {
                    $connection->close();
                }
                // onWebSocketConnect 里面$_GET $_SERVER是可用的
                // var_dump($_GET, $_SERVER);
            };
        };
        */
    }

    /**
     * 配置businessworker
     * @param string $register_address 注册地址
     */
    private function conf_businussworker($register_address = '127.0.0.1:1236')
    {
        // bussinessWorker 进程
        $worker = new BusinessWorker();
        // worker名称
        $worker->name = 'ChatBusinessWorker';
        // bussinessWorker进程数量
        $worker->count = 1;
        // 服务注册地址
        $worker->registerAddress = $register_address;
        //设置log目录，当前目录的 workerman.log（注意只有以 daemon方式运行时，才有效）

        if (Env::get('host.name') === 'online')
        {//线上多台服务器需要这样配置一下
//            $worker::$pidFile = '/mnt/run/workerman/workerman-' . microtime(true) . '.pid';
            $worker::$pidFile = '/var/run/workerman.pid';
            $worker::$stdoutFile = config('websocket.workerman_log_path') . DS . 'workerman.log';
        } else
        {
            $worker::$stdoutFile = __DIR__ . '/workerman.log';
        }
        /*
         * 参考：http://doc2.workerman.net/business-worker.html
         * 设置处理业务的类为MyEvent。
         * 如果类带有命名空间，则需要把命名空间加上，
         * 类似$worker->eventHandler='\my\namespace\MyEvent';
         */
        $worker->eventHandler = 'app\chat\MyEvent';
    }
}
